#include <iostream>
#include "l_utility.hpp"
#include "server.hpp"
#include <boost/asio.hpp>

namespace http {
namespace server3 {

static int l_inet_network(lua_State* L) {
	if (lua_isstring(L, 1)) {
		in_addr_t ipl = 0;
		if ((ipl = inet_network(luaL_checkstring(L,1))) == INADDR_NONE) {
			std::cout <<"ERROR: l_inet_network: inet_network(luaL_checkstring(L,1))) == INADDR_NONE "<<std::endl;
			return 0;
		}
		char buf[32];
		char *ptr = buf;
		char *p = buf + 31;
		size_t l;
		do {
			*p-- = '0' + (ipl % 10);
			ipl /= 10;
		} while (ipl);
		p++;
		l = buf + 32 - p;
		while (l--)
			*ptr++ = *p++;
		lua_pushlstring(L, buf, ptr - buf);
		return 1;
	}
	return 0;
}

static int lua_sleep(lua_State *L) {
	//printf("ready to receive new data \n");
	return lua_yield(L, 0);
}

int l_register_table_SI(lua_State* L, int n, Util_Table_SI *arr,
		const char* table_name) {
	lua_newtable(L);
	for (; arr->key; arr++) {
		lua_pushstring(L, arr->key);
		lua_pushinteger(L, arr->value);
		lua_rawset(L, -3);
	}
	//Tao mot ban sao của metatable
	lua_pushvalue(L, -1);
	lua_setfield(L, -1, "__index");
	lua_setfield(L, n - 1, table_name);
	return 1;
}

/* Converts a hex character to its integer value */
static inline char from_hex(char ch) {
	return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
}

/* Converts an integer value to its hex character*/
static inline char to_hex(char code) {
	static char hex[] = "0123456789abcdef";
	return hex[code & 15];
}

/* Returns a url-encoded version of str */
/* IMPORTANT: be sure to free() the returned string after use */
static char *fast_url_encode(const char *str) {
	const char *pstr = str;
	char *buf = (char*) malloc(strlen(str) * 3 + 1), *pbuf = buf;
	while (*pstr) {
		if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.'
				|| *pstr == '~')
			*pbuf++ = *pstr;
		else if (*pstr == ' ')
			*pbuf++ = '+';
		else
			*pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(
					*pstr & 15);
		pstr++;
	}
	*pbuf = '\0';
	return buf;
}

/* Returns a url-decoded version of str */
/* IMPORTANT: be sure to free() the returned string after use */
static char *fast_url_decode(const char *str) {
	const char *pstr = str;
	char *buf = (char*) malloc(strlen(str) + 1), *pbuf = buf;
	while (*pstr) {
		if (*pstr == '%') {
			if (pstr[1] && pstr[2]) {
				*pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
				pstr += 2;
			}
		} else if (*pstr == '+') {
			*pbuf++ = ' ';
		} else {
			*pbuf++ = *pstr;
		}
		pstr++;
	}
	*pbuf = '\0';
	return buf;
}

static int l_fast_url_decode(lua_State *L) {
	if (lua_isstring(L, 1)) {
		char* buf = fast_url_decode(luaL_checkstring(L, 1));
		lua_pushstring(L, buf);
		free(buf);
		return 1;
	}
	return 0;
}
static int l_fast_url_encode(lua_State *L) {
	if (lua_isstring(L, 1)) {
		char* buf = fast_url_encode(luaL_checkstring(L, 1));
		lua_pushstring(L, buf);
		free(buf);
		return 1;
	}
	return 0;
}
static int l_parse_KV(lua_State* L) {
	if (lua_isstring(L, 1) && lua_isstring(L, 2)) {
		const char *uri = luaL_checkstring(L,1);
		// param_seperator
		const char *seperators;
		seperators = luaL_optstring(L,2,"&=");
		const char *ptr = uri;
		int pushed = 0;
		const char* ptrsep;
		lua_createtable(L, 0, 4); // preallocate 8 table entries
        do {
			ptrsep = seperators; // reset ptrsep
            while (*ptr != *ptrsep && *ptrsep) ptrsep++; // compare every char
            if (!*ptrsep) continue; // not found
            lua_pushlstring(L, uri, ptr - uri);
            while (*ptr == ' ') ptr++; // ignore space
            uri = ptr+1;
            pushed++;
			if (pushed == 2) { // have key and value
				lua_rawset(L, -3);
				pushed = 0;
			}
        }while (*++ptr);
        if (pushed == 1) { // last value
			lua_pushlstring(L, uri, ptr - uri);
			lua_rawset(L, -3);
		}
	} else
		lua_newtable(L);
	return 1;
}
static int l_parse(lua_State* L) {
	if (lua_isstring(L, 1) && lua_isstring(L, 2)) {
		const char *uri = luaL_checkstring(L,1);
		// param_seperator
		const char *seperators;
		seperators = luaL_optstring(L,2,";");
		const char *ptr = uri;
		const char* ptrsep;
		int paramID = 1;
		lua_createtable(L, 8, 0); // preallocate 8 table entries
        do {
			ptrsep = seperators; // reset ptrsep
            while (*ptr != *ptrsep && *ptrsep) ptrsep++; // compare every char
            if (!*ptrsep) continue; // not found
			lua_pushnumber(L, paramID++);
            lua_pushlstring(L, uri, ptr - uri);
			lua_rawset(L, -3);
            while (*ptr == ' ') ptr++; // ignore space
            uri = ptr+1;
        }while (*++ptr);
        if (ptr != uri) { // last value
			lua_pushnumber(L, paramID);
			lua_pushlstring(L, uri, ptr - uri);
			lua_rawset(L, -3);
		}
	} else
		lua_newtable(L);
	return 1;
}
static int l_parse_len(lua_State* L) {
	int len = 0;
	if (lua_isstring(L, 1) && lua_isstring(L, 2)) {
		const char *uri = luaL_checkstring(L,1);
		// param_seperator
        const char *seperators = luaL_optstring(L,2,";");
		int max = luaL_optnumber(L, 3, INT_MAX);
		const char *ptr = uri;
		const char* ptrsep;
        do{
			ptrsep = seperators; // reset ptrsep
            while (*ptr != *ptrsep && *ptrsep) ptrsep++; // compare every char
            if (!*ptrsep) continue; // not found
            lua_pushlstring(L, uri, ptr - uri);
            if (++len == max) break;
            while (*ptr == ' ') ptr++; // ignore space
            uri = ptr+1;
        }while (*++ptr);
		if (ptr != uri && len < max) { // last value
			lua_pushlstring(L, uri, ptr - uri);
			len++;
		}
		lua_pushnumber(L, len);
	}
	return len + 1;
}
static int l_count(lua_State* L) {
    int count = 0;
    if (lua_isstring(L, 1) && lua_isstring(L, 2)) {
        const char *ptr = luaL_checkstring(L,1);
        // param_seperator
        const char *str = luaL_checkstring(L,2);
        char ch = str[0];
        do{
            if(*ptr==ch) count++;
        }while (*++ptr);
    }
    lua_pushnumber(L, count);
    return 1;
}
static int l_create_guid(lua_State *L) {
	size_t len;
	const char* t = luaL_checklstring(L, 1, &len);
	const char* ip = luaL_checkstring(L,2);
	if (len > 10)
		len = 9;
	else
		len--;
	char guid[64];
	char *ptr = guid;
	while (len--)
		*ptr++ = *t++;
	in_addr_t ipl = 0;
	if ((ipl = inet_network(ip)) == -1) {
		std::cout<<"ERROR: l_create_guid: inet_network(luaL_checkstring(L,1))) == INADDR_NONE, IP ="<<ip<<std::endl;
		return 0;
	}
	char *p = guid + 63;
	size_t l;
	do {
		*p-- = '0' + (ipl % 10);
		ipl /= 10;
	} while (ipl);
	p++;
	l = guid + 64 - p;
	while (l--)
		*ptr++ = *p++;
	lua_pushlstring(L, guid, ptr - guid);
	return 1;
}

// Create a random permutation using Fisher-Yates Shuffle algorithm
static int l_create_permutation(lua_State *L) {
    if(lua_isnumber(L,1)) {
        int n = luaL_checknumber(L,1)+1;
        lua_createtable(L,n,0);
        int *arr = (int*)malloc(n*sizeof(int));
        for(int i=1;i<n;i++) arr[i]=i;
        int tmp,k;
        srand((unsigned)time(NULL));
        for(int i=n-1;i>0;i--){
            k = rand()%i+1;
            tmp=arr[i];
            arr[i]=arr[k];
            arr[k]=tmp;
        }
        for(int i=1;i<n;i++) {
            lua_pushnumber(L,i);
            lua_pushnumber(L,arr[i]);
            lua_rawset(L,-3);
        }
    }
    else lua_newtable(L);
    return 1;
}
static int l_create_hashtable(lua_State* L) {
    if (lua_isstring(L, 1) && lua_isstring(L, 2)) {
        const char *uri = luaL_checkstring(L,1);
        // param_seperator
        const char *seperators;
        seperators = luaL_optstring(L,2,";");
        const char *ptr = uri;
        const char* ptrsep;
        int paramID = 1;
        lua_createtable(L, 8, 0); // preallocate 8 table entries
        do {
            ptrsep = seperators; // reset ptrsep
            while (*ptr != *ptrsep && *ptrsep) ptrsep++; // compare every char
            if (!*ptrsep) continue; // not found
            lua_pushlstring(L, uri, ptr - uri);
            lua_pushnumber(L, paramID++);
            lua_rawset(L, -3);
            while (*ptr == ' ') ptr++; // ignore space
            uri = ptr+1;
        }while (*++ptr);
        if (ptr != uri) { // last value
            lua_pushlstring(L, uri, ptr - uri);
            lua_pushnumber(L, paramID);
            lua_rawset(L, -3);
        }
    } else
        lua_newtable(L);
    return 1;
}
///////////////////////////////////////////////////////////////
//input:business's name: zc.1, zc.2, adv or anything with a name
//output: nothing
///////////////////////////////////////////////////////////////
static int l_log_business_count(lua_State* L) {
	if (lua_isstring(L, 1)){
		std::map<std::string,int>::iterator it;
		std::string name = std::string(luaL_checkstring(L,1));
		server::log_business_count_impl(name);	}
	return 0;
}
static int l_log_get_business_count(lua_State* L){
	if (lua_isstring(L, 1)){
		std::string name = std::string(luaL_checkstring(L,1));
		int result = server::log_get_business_count_impl(name);
		if (result == -1)
			return 0;
		else{
			lua_pushnumber(L, result);
			return 1;
		}
	}else{
		return 0;
	}
}
static int l_log_reset_business_count(lua_State* L){
	if (lua_isstring(L, 1)){
		std::string name = std::string(luaL_checkstring(L,1));
		server::log_reset_business_count_impl(name);
	}
	return 0;
}
static int l_log_reset_log_time(lua_State* L){
	server::log_time = server::currentDateTime();
	return 0;
}
static int l_log_get_log_time(lua_State* L){
	lua_pushlstring(L, server::log_time.c_str(),server::log_time.length());
	return 1;
}
static luaL_reg regArr[] = {
    { "wait_for_next_request", lua_sleep },
    { "c_inet_network", l_inet_network },
    { "fast_url_decode", l_fast_url_decode },
    { "fast_url_encode", l_fast_url_encode },
    { "parseKV", l_parse_KV },
    { "parse", l_parse },
    { "parseLen", l_parse_len },
    { "count", l_count },
    { "create_guid", l_create_guid },
    { "create_permutation", l_create_permutation },
    { "create_hashtable", l_create_hashtable },
    { "log_business_count", l_log_business_count},
    { "log_get_business_count", l_log_get_business_count},
    { "log_reset_business_count", l_log_reset_business_count},
    { "log_reset_log_time", l_log_reset_log_time},
    { "log_get_log_time", l_log_get_log_time},
    { NULL, NULL } };
//===============================================
int l_register_utility(lua_State* L) {
	luaL_register(L, "util", regArr);
	return 1;
}

}
}

